/*
    This file is part of SQLiteGTK, written by Aly Hirani

    SQLiteGTK is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SQLiteGTK is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SQLiteGTK.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "include.h"
#include "plugin_loader.h"
#include "util.h"

/*
static GtkWidget* plugin_tab_header_new(const gchar* title)
{
	return gtk_label_new(title);
}
*/

#if 0
/* returns the id of the tab created. -1, if not */
gint plugin_new_tabs(GtkNotebook *notebook, const NotebookPlugin *plugin, const NotebookActionType action_type, const gchar** path, GList **plugin_list)
{
	NotebookPluginTab *tabs;
	GtkWidget *header;
	NotebookAction action;
	guint i, count;
	
	D_ASSERT(notebook);
	D_ASSERT(plugin);
	D_ASSERT(path);

	action.type = action_type;
	action.path = path;
	
	count = voidv_length((void**)path);
	D_ASSERT(count > 0);
	tabs = g_malloc_types(NotebookPluginTab, count);
	R_ASSERT(tabs);

	if(!plugin->func_action(&action, tabs, count))
	{
        g_free(tabs);
		log_warning("Unable to create %d tabs. The plugin %s responded with a FALSE", count, plugin->info->guid);
		return -1;
	}

	/* Let's perform some validation on the output of the plugin and then add it to our notebook and plugins_list */
	for(i = 0; i < count; ++i)
	{
		
	}
	

	g_assert(tab->page);

	switch(tab->header_type)
	{
		case NHT_STANDARD:
			header = create_tab_header(tab->header_data.title);
			break;
		case NHT_CUSTOM:
			header = tab->header_data.header;
			break;
	}
	g_assert(header);

	gtk_label_set_text(GTK_LABEL(tab->page), "Hello World");
	return gtk_notebook_append_page(notebook, tab->page, header);
}
#endif

static inline gboolean load_plugin_symbol(GModule *module, const gchar* symbol_name, gpointer* dest)
{
	return g_module_symbol(module, symbol_name, dest) && *dest;
}

/* Pass 1: Done */
static NotebookPlugin *load_plugin(const gchar *path)
{
	static int plugin_count = 0;
	NotebookPluginInfo* plugin_info;
	NotebookPlugin *plugin = g_malloc_type(NotebookPlugin);
	GModule *module = g_module_open(path, G_MODULE_BIND_MASK);
	
	R_ASSERT(plugin);

	if(!module)
		goto cleanup;
	
	if(!load_plugin_symbol(module, "plugin_get_info", (gpointer*)&plugin->func_get_info))
	{
		log_warning("Unable to find the plugin_get_info symbol");
		goto cleanup;
	}
	
	if(!load_plugin_symbol(module, "plugin_free_info", (gpointer*)&plugin->func_free_info))
	{
		log_warning("Unable to find the plugin_free_info symbol");
		goto cleanup;
	}

	if(!load_plugin_symbol(module, "plugin_activate", (gpointer*)&plugin->func_activate))
	{
		log_warning("Unable to find the plugin_activate symbol");
		goto cleanup;
	}

	if(!load_plugin_symbol(module, "plugin_deactivate", (gpointer*)&plugin->func_deactivate))
	{
		log_warning("Unable to find the plugin_deactivate symbol");
		goto cleanup;
	}

	if(!load_plugin_symbol(module, "plugin_action", (gpointer*)&plugin->func_action))
	{
		log_warning("Unable to find the plugin_action symbol");
		goto cleanup;
	}

	plugin_info = plugin->func_get_info();
	
	if(!plugin_info || !plugin_info->guid)
	{
		log_warning("plugin_get_info() failed");
		goto cleanup;
	}
	   
	plugin->id = plugin_count++;
	plugin->module = module;
	plugin->info = plugin_info;
	
	return plugin;

cleanup:
	if(module)
	{
		log_warning("Failed to load plugin %s successfully. GModuleError: %s", path, g_module_error());

		if(!g_module_close(module))
		{
			log_warning("Unable to unload plugin %s. GModuleError: %s", path, g_module_error());
		}
	}

	return NULL;
}

/* Pass 1: Done */
static GList *load_dir_plugins(const gchar *dir_path, GList *plugins)
{
	const gchar *file_name;
	GDir *dir;
	
	R_ASSERT(dir_path);
	
	dir = g_dir_open(dir_path, 0, NULL);
	if(!dir)
	{
		log_warning("Unable to open GDir to load plugins for %s", dir_path);
		return plugins;
	}
	
	while((file_name = g_dir_read_name(dir)) != NULL)
	{
		NotebookPlugin *plugin;
		gchar *path = g_build_path(G_DIR_SEPARATOR_S, dir_path, file_name, NULL);
		R_ASSERT(path);
		
		plugin = load_plugin(path);
		if(plugin)
			plugins = g_list_prepend(plugins, plugin);

		g_free(path);
	}
	
	g_dir_close(dir);
	
	return plugins;
}

/* Pass 1: Done */
GList* load_plugins(const gchar **path)
{
	GList *list = NULL;
	guint i = 0;
	const gchar* current_dir;

	D_ASSERT(path);

	while((current_dir = path[i++]))
	{
		list = load_dir_plugins(current_dir, list);
	}

	return list;
}

#if 0
struct _FindDependsResult
{
	gboolean success;
	union
	{
		GList *depends;
		struct {
			GUID *guid1;
			GUID *guid2;
		} conflict;
	} data;
};

typedef struct _FindDependsResult FindDependsResult;

static void find_depends(GUID* plugin_guid, GList *plugin_list, GList **depends_list, GList **excluded_list, FindDependsResult *result)
{
	GList *iter;

	D_ASSERT(plugin_guid && plugin_list && depends_list && excluded_list);

	if(!result)
	{
		result = g_malloc_type(FindDependsResult);
		R_ASSERT(result);

		result->success = TRUE;
	}
	else

	*excluded_list = g_list_prepend(*excluded_list, plugin_guid);

	for(iter = plugin_list; iter != NULL; iter = iter->next)
	{
		if(guid_equal(((NotebookPlugin*)iter->data)->info->guid, plugin_guid))
		{
			GList *iter2;

			for(iter2 = ((NotebookPlugin*)iter->data)->info->depends; iter2 != NULL; iter2 = iter2->next)
			{
				GList *guid_search = g_list_find(*excluded_list, iter2->data);

				if(guid_search)
				{
					/* We have cyclic dependencies. Dammit */
					result->success = FALSE;
					result->data.conflict.guid1 = guid_search->data;
					result->data.conflict.guid2 = (GUID*)iter2->data;
					return;
				}
				else
				{
					find_depends(((NotebookPlugin*)iter->data)->info->guid, plugin_list, depends_list, excluded_list, result);

					if(!result->success)
						return;
				}
			}
		}
	}
}
#endif 

#if 0
static void unload_plugin(NotebookPlugin *plugin)
{
	D_ASSERT(plugin);
	D_ASSERT(plugin->module);

	/* Find all depending plugin */
}

static void unload_plugins_foreach(gpointer data, gpointer user_data)
{
	/*
	TODO: implement this function
	*/
}

void unload_plugins(GList *plugins)
{
	g_list_foreach(plugins, (GFunc)unload_plugins_foreach, NULL);
	g_list_free(plugins);
}
#endif
